﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace NHINDirect
{    
    /// <summary>
    /// Default Entity serialization
    /// </summary>
    public class EntitySerializer
    {
        public static readonly EntitySerializer Default = new EntitySerializer();
        
        public EntitySerializer()
        {
        }
        
        public void Serialize(MimeEntity entity, string filePath)
        {
            using(Stream stream = File.OpenWrite(filePath))
            {
                this.Serialize(entity, stream);
            }
        }
        
        public void Serialize(MimeEntity entity, Stream stream)
        {
            using (StreamWriter writer = new StreamWriter(stream, Encoding.ASCII))
            {
                this.Serialize(entity, writer);
            }
        }
        
        public void Serialize(MimeEntity entity, TextWriter writer)
        {
            if (entity == null)
            {
                throw new ArgumentNullException();
            }
            
            EntityWriter entityWriter = new EntityWriter(writer);
            if (entity.HasHeaders)
            {
                entityWriter.Write(entity.Headers);
                entityWriter.WriteCRLF();
            }
            if (entity.HasBody)
            {
                entityWriter.Write(entity.Body);
                if (entity is Message)
                {
                    entityWriter.WriteCRLF();
                }
            }
        }

        public void Serialize(MimeEntity entity, EntityWriter entityWriter)
        {
            if (entity == null || entityWriter == null)
            {
                throw new ArgumentNullException();
            }

            if (entity.HasHeaders)
            {
                entityWriter.Write(entity.Headers);
                entityWriter.WriteCRLF();
            }
            if (entity.HasBody)
            {
                entityWriter.Write(entity.Body);
                if (entity is Message)
                {
                    entityWriter.WriteCRLF();
                }
            }
        }
        
        public string Serialize(MimeEntity entity)
        {
            byte[] asciiBytes = this.SerializeToBytes(entity);
            return Encoding.ASCII.GetString(asciiBytes);
        }
        
        public byte[] SerializeToBytes(MimeEntity entity)
        {
            using (MemoryStream stream = new MemoryStream())
            {
                this.Serialize(entity, stream);
                return stream.ToArray();
            }
        }

        public void Serialize(IEnumerable<MimeEntity> entities, string boundary, TextWriter writer)
        {
            if (entities == null)
            {
                throw new ArgumentNullException();
            }
            
            EntityWriter entityWriter = new EntityWriter(writer);
            foreach(MimeEntity entity in entities)
            {
                entityWriter.WriteMimeBoundary(boundary, false);
                this.Serialize(entity, entityWriter);
            }
            entityWriter.WriteMimeBoundary(boundary, true);
        }

        public string Serialize(IEnumerable<MimeEntity> entities, string boundary)
        {
            byte[] asciiBytes = this.SerializeToBytes(entities, boundary);
            return Encoding.ASCII.GetString(asciiBytes);
        }

        public void Serialize(IEnumerable<MimeEntity> entities, string boundary, Stream stream)
        {
            using (StreamWriter writer = new StreamWriter(stream, Encoding.ASCII))
            {
                this.Serialize(entities, boundary, writer);
            }
        }

        public byte[] SerializeToBytes(IEnumerable<MimeEntity> entities, string boundary)
        {
            using (MemoryStream stream = new MemoryStream())
            {
                this.Serialize(entities, boundary, stream);
                return stream.ToArray();
            }
        }

        public T Deserialize<T>(Stream stream)
            where T : MimeEntity, new()
        {
            if (stream == null)
            {
                throw new ArgumentException();
            }

            using (StreamReader reader = new StreamReader(stream, Encoding.ASCII))
            {
                return this.Deserialize<T>(reader);
            }
        }
        
        public T Deserialize<T>(TextReader reader)
            where T : MimeEntity, new()
        {
            if (reader == null)
            {
                throw new ArgumentNullException();
            }
            
            return this.Deserialize<T>(reader.ReadToEnd());
        }

        public T Deserialize<T>(string messageText)
            where T : MimeEntity, new()
        {
            if (string.IsNullOrEmpty(messageText))
            {
                throw new ArgumentNullException();
            }
            
            return this.Deserialize<T>(new StringSegment(messageText));
        }

        public T Deserialize<T>(StringSegment messageText)
            where T : MimeEntity, new()
        {
            return EntityParser.Read<T>(messageText);
        }
        
        public T Deserialize<T>(byte[] messageBytes)
            where T : MimeEntity, new()
        {
            if (messageBytes == null || messageBytes.Length == 0)
            {
                throw new ArgumentException();
            }
            
            using (MemoryStream stream = new MemoryStream(messageBytes))
            {
                return this.Deserialize<T>(stream);
            }            
        }
    }
}
